
'use strict';

!function () {
    const config = {
        threshold: 1,
        drawBobOutlinePointsDelay: 10,
        drawContourPointsDelay: 100,
        imageDataSize: 2
    }

    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.src = './白玉无冰.png';

    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    img.onload = function () {
        main();
        // main_rdp_debug();
    };

    const main = function () {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(img, 0, 0);
        const canvasImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = canvasImageData.data;
        let blobOutlinePoints = marching_squares.getBlobOutlinePoints(data, canvas.width, canvas.height, true);
        console.log('blobOutlinePoints', blobOutlinePoints);

        const imageData = ctx.createImageData(config.imageDataSize, config.imageDataSize);
        for (let i = 0; i < imageData.data.length; i += 4) {
            // Modify pixel data
            imageData.data[i + 0] = 255;  // R value
            imageData.data[i + 1] = 0;    // G value
            imageData.data[i + 2] = 0;  // B value
            imageData.data[i + 3] = 255;  // A value
        }
        for (let index = 0; index < blobOutlinePoints.length; index++) {
            setTimeout(() => {
                const point = blobOutlinePoints[index];
                // Draw image data to the canvas
                ctx.putImageData(imageData, point.x, point.y);
            }, config.drawBobOutlinePointsDelay * index);
        }

        const contourPoints = rdp(blobOutlinePoints, config.threshold);
        if (contourPoints[0] && contourPoints[contourPoints.length - 1] && contourPoints[0].x === contourPoints[contourPoints.length - 1].x && contourPoints[0].y === contourPoints[contourPoints.length - 1].y) {
            // 去掉重复点 
            contourPoints.length = contourPoints.length - 1;
        }
        console.log('contourPoints', contourPoints);


        const imageDataPoint = ctx.createImageData(config.imageDataSize, config.imageDataSize);
        for (let i = 0; i < imageDataPoint.data.length; i += 4) {
            // Modify pixel data
            imageDataPoint.data[i + 0] = 0;  // R value
            imageDataPoint.data[i + 1] = 0;    // G value
            imageDataPoint.data[i + 2] = 255;  // B value
            imageDataPoint.data[i + 3] = 255;  // A value
        }
        ctx.strokeStyle = 'blue';
        ctx.lineWidth = 3;
        for (let index = 0; index < contourPoints.length; index++) {
            const point = contourPoints[index];
            setTimeout(() => {
                ctx.putImageData(imageDataPoint, point.x, point.y);
            }, blobOutlinePoints.length * config.drawBobOutlinePointsDelay + config.drawContourPointsDelay * index);
        }
    };

    const main_rdp_debug = function () {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(img, 0, 0);
        const canvasImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = canvasImageData.data;
        let blobOutlinePoints = marching_squares.getBlobOutlinePoints(data, canvas.width, canvas.height, true);
        console.log('blobOutlinePoints', blobOutlinePoints);

        const imageData = ctx.createImageData(config.imageDataSize, config.imageDataSize);
        for (let i = 0; i < imageData.data.length; i += 4) {
            // Modify pixel data
            imageData.data[i + 0] = 255;  // R value
            imageData.data[i + 1] = 0;    // G value
            imageData.data[i + 2] = 0;  // B value
            imageData.data[i + 3] = 255;  // A value
        }
        for (let index = 0; index < blobOutlinePoints.length; index++) {
            setTimeout(() => {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(img, 0, 0);
                const contourPoints = rdp_debug(blobOutlinePoints, config.threshold, index);
                console.log('contourPoints', index, contourPoints.length);
                for (let index2 = 0; index2 < contourPoints.length; index2++) {
                    const point = contourPoints[index2];
                    ctx.putImageData(imageData, point.x, point.y);
                }
            }, 500 * index);
        }
    };

    const marching_squares = {
        NONE: 0,
        UP: 1,
        LEFT: 2,
        DOWN: 3,
        RIGHT: 4,
        getBlobOutlinePoints: function (data, width, height, loop) {
            marching_squares.data = data,
                marching_squares.width = width,
                marching_squares.height = height,
                marching_squares.loop = loop;
            var p = marching_squares.getFirstNonTransparentPixelTopDown(),
                result = marching_squares.walkPerimeter(p.x, p.y);
            return marching_squares.width = null,
                marching_squares.height = null,
                marching_squares.data = null,
                marching_squares.loop = null,
                result
        },
        getFirstNonTransparentPixelTopDown: function () {
            var t, a, data = marching_squares.data,
                width = marching_squares.width,
                height = marching_squares.height,
                s = 0;
            for (t = 0; t < height; t++) for (a = 0; a < width; a++, s += 4) if (data[s + 3] > 0) return {
                x: a,
                y: t
            };
            return null
        },
        walkPerimeter: function (pos_x, pos_y) {
            pos_x < 0 && (pos_x = 0),
                pos_x > marching_squares.width && (pos_x = marching_squares.width),
                pos_y < 0 && (pos_y = 0),
                pos_y > marching_squares.height && (pos_y = marching_squares.height);
            var x = pos_x,
                y = pos_y,
                result = [{ x, y }];
            do {
                switch (marching_squares.step(x, y, marching_squares.data), marching_squares.nextStep) {
                    case marching_squares.UP:
                        y--;
                        break;
                    case marching_squares.LEFT:
                        x--;
                        break;
                    case marching_squares.DOWN:
                        y++;
                        break;
                    case marching_squares.RIGHT:
                        x++
                }
                x >= 0 && x <= marching_squares.width && y >= 0 && y <= marching_squares.height && result.push({ x, y })
            } while (x !== pos_x || y !== pos_y);
            return marching_squares.loop && result.push({ x, y }),
                result
        },
        step: function (x, y, data) {
            var width = marching_squares.width,
                rowOffset = 4 * width,
                upLeftIndex = (y - 1) * rowOffset + 4 * (x - 1),
                isInLeft = x > 0,
                isInRight = x < width,
                isInDown = y < marching_squares.height,
                isInUp = y > 0;
            /*
            checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent
            +---+---+
            | 1 | 2 |
            +---+---+
            | 4 | 8 | <- current pixel (curx,cury)
            +---+---+
            */
            marching_squares.upLeft = isInUp && isInLeft && data[upLeftIndex + 3] > 0;
            marching_squares.upRight = isInUp && isInRight && data[upLeftIndex + 7] > 0;
            marching_squares.downLeft = isInDown && isInLeft && data[upLeftIndex + rowOffset + 3] > 0;
            marching_squares.downRight = isInDown && isInRight && data[upLeftIndex + rowOffset + 7] > 0;
            marching_squares.previousStep = marching_squares.nextStep;
            marching_squares.state = 0;
            marching_squares.upLeft && (marching_squares.state |= 1);
            marching_squares.upRight && (marching_squares.state |= 2);
            marching_squares.downLeft && (marching_squares.state |= 4);
            marching_squares.downRight && (marching_squares.state |= 8);
            /* going DOWN with these cases:
                8          10          11
                +---+---+  +---+---+   +---+---+
                |   |   |  |   | 2 |   | 1 | 2 |
                +---+---+  +---+---+   +---+---+
                |   | 8 |  |   | 8 |   |   | 8 |
                +---+---+  +---+---+  	+---+---+
            */

            /* going UP with these cases:
                1          5           13
                +---+---+  +---+---+  +---+---+ 
                | 1 |   |  | 1 |   |  | 1 |   | 
                +---+---+  +---+---+  +---+---+ 
                |   |   |  | 4 |   |  | 4 | 8 | 
                +---+---+  +---+---+  +---+---+
            */

            /* going LEFT with these cases:
                4          12          14
                +---+---+  +---+---+   +---+---+
                |   |   |  |   |   |   |   | 2 |
                +---+---+  +---+---+   +---+---+
                | 4 |   |  | 4 | 8 |   | 4 | 8 |
                +---+---+  +---+---+  	+---+---+
            */

            /* going RIGHT with these cases:
                2          3           7        
                +---+---+  +---+---+   +---+---+
                |   | 2 |  | 1 | 2 |   | 1 | 2 |
                +---+---+  +---+---+   +---+---+
                |   |   |  |   |   |   | 4 |   |
                +---+---+  +---+---+  	+---+---+
            */

            /*
                6
                +---+---+
                |   | 2 |
                +---+---+
                | 4 |   |
                +---+---+
                当上一步是往上 则这一步往右，否则往左
            */

            /*
                9
                +---+---+
                | 1 |   |
                +---+---+
                |   | 8 |
                +---+---+
                当上一步是往右 则这一步往下，否则往上
            */

            switch (marching_squares.state) {
                case 1:
                    marching_squares.nextStep = marching_squares.UP;
                    break;
                case 2:
                case 3:
                    marching_squares.nextStep = marching_squares.RIGHT;
                    break;
                case 4:
                    marching_squares.nextStep = marching_squares.LEFT;
                    break;
                case 5:
                    marching_squares.nextStep = marching_squares.UP;
                    break;
                case 6:
                    marching_squares.previousStep == marching_squares.UP ? marching_squares.nextStep = marching_squares.LEFT : marching_squares.nextStep = marching_squares.RIGHT;
                    break;
                case 7:
                    marching_squares.nextStep = marching_squares.RIGHT;
                    break;
                case 8:
                    marching_squares.nextStep = marching_squares.DOWN;
                    break;
                case 9:
                    marching_squares.previousStep == marching_squares.RIGHT ? marching_squares.nextStep = marching_squares.UP : marching_squares.nextStep = marching_squares.DOWN;
                    break;
                case 10:
                case 11:
                    marching_squares.nextStep = marching_squares.DOWN;
                    break;
                case 12:
                    marching_squares.nextStep = marching_squares.LEFT;
                    break;
                case 13:
                    marching_squares.nextStep = marching_squares.UP;
                    break;
                case 14:
                    marching_squares.nextStep = marching_squares.LEFT;
                    break;
                default:
                    marching_squares.nextStep = marching_squares.NONE
            }
        }
    };
    const pointLineDistance = function (point, linePointBegin, linePointEnd) {
        let result, k, b;
        if (linePointBegin.x === linePointEnd.x) {
            result = Math.abs(point.x - linePointBegin.x)
        } else {
            // y = kx + b
            k = (linePointEnd.y - linePointBegin.y) / (linePointEnd.x - linePointBegin.x);
            b = linePointBegin.y - k * linePointBegin.x;
            // kx - y + b = 0
            result = Math.abs(k * point.x - point.y + b) / Math.sqrt(Math.pow(k, 2) + 1);
        }
        return result
    }
    const rdp = function rdp(points, threshold) {
        var point0 = points[0],
            point_end = points[points.length - 1];
        if (points.length < 3) return points;
        for (var slice_index = -1, max_dis = 0, index = 1; index < points.length - 1; index++) {
            var cur_dis = pointLineDistance(points[index], point0, point_end);
            cur_dis > max_dis && (max_dis = cur_dis, slice_index = index)
        }
        if (max_dis > threshold) {
            var left_poins = points.slice(0, slice_index + 1),
                right_points = points.slice(slice_index),
                left_rdp = rdp(left_poins, threshold),
                right_rdp = rdp(right_points, threshold);
            return left_rdp.slice(0, left_rdp.length - 1).concat(right_rdp)
        }
        return [point0, point_end]
    };

    const rdp_debug = function rdp_debug(points, threshold, count) {
        var point0 = points[0],
            point_end = points[points.length - 1];
        if (points.length < 3 || count <= 0) return points;
        for (var slice_index = -1, max_dis = 0, index = 1; index < points.length - 1; index++) {
            var cur_dis = pointLineDistance(points[index], point0, point_end);
            cur_dis > max_dis && (max_dis = cur_dis, slice_index = index)
        }
        if (max_dis > threshold) {
            var left_poins = points.slice(0, slice_index + 1),
                right_points = points.slice(slice_index),
                left_rdp = rdp_debug(left_poins, threshold, count - 1),
                right_rdp = rdp_debug(right_points, threshold, count - 1);
            return left_rdp.slice(0, left_rdp.length - 1).concat(right_rdp)
        }
        return [point0, point_end]
    };
}()

console.warn(`
        欢迎关注微信公众号  白玉无冰 

        导航：https://mp.weixin.qq.com/s/Ht0kIbaeBEds_wUeUlu8JQ
        `)
console.info(
    `
            █████████████████████████████████████
            █████████████████████████████████████
            ████ ▄▄▄▄▄ █▀█ █▄██▀▄ ▄▄██ ▄▄▄▄▄ ████
            ████ █   █ █▀▀▀█ ▀▄▀▀▀█▄▀█ █   █ ████
            ████ █▄▄▄█ █▀ █▀▀▀ ▀▄▄ ▄ █ █▄▄▄█ ████
            ████▄▄▄▄▄▄▄█▄▀ ▀▄█ ▀▄█▄▀ █▄▄▄▄▄▄▄████
            ████▄▄  ▄▀▄▄ ▄▀▄▀▀▄▄▄ █ █ ▀ ▀▄█▄▀████
            ████▀ ▄  █▄█▀█▄█▀█  ▀▄ █ ▀ ▄▄██▀█████
            ████ ▄▀▄▄▀▄ █▄▄█▄ ▀▄▀ ▀ ▀ ▀▀▀▄ █▀████
            ████▀ ██ ▀▄ ▄██ ▄█▀▄ ██▀ ▀ █▄█▄▀█████
            ████   ▄██▄▀ █▀▄▀▄▀▄▄▄▄ ▀█▀ ▀▀ █▀████
            ████ █▄ █ ▄ █▀ █▀▄█▄▄▄▄▀▄▄█▄▄▄▄▀█████
            ████▄█▄█▄█▄█▀ ▄█▄   ▀▄██ ▄▄▄ ▀   ████
            ████ ▄▄▄▄▄ █▄██ ▄█▀  ▄   █▄█  ▄▀█████
            ████ █   █ █ ▄█▄ ▀  ▀▀██ ▄▄▄▄ ▄▀ ████
            ████ █▄▄▄█ █ ▄▄▀ ▄█▄█▄█▄ ▀▄   ▄ █████
            ████▄▄▄▄▄▄▄█▄██▄▄██▄▄▄█████▄▄█▄██████
            █████████████████████████████████████
            █████████████████████████████████████
            `
)
